﻿/*
 *  ChgDoc CMS - GNU General Public License, version 3 (GPL-3.0)
 *  Copyright (C)  2012. Ian Quigley
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using System.IO;
using System.Configuration;
using ChgDoc.Extension;
using ChgDoc.Render;
using ChgDoc.Entity;

namespace ChgDoc.Edit
{
	[AdminPage( "Setup Tasks")]
    public partial class SetupTasks : Page
	{
        public enum TaskState { Ok, Warning, Error, Stop, Unknown }

        public class SetupTask
        {
            public string Code;
            public string Description;
            public string Resolution;

            private TaskState _state = TaskState.Unknown;
            public TaskState State { get { if (_state == TaskState.Unknown) _state = GetState(); return _state; } set { _state = value; } }
            internal Func<TaskState> GetState;

            public Action AutoFix;
        }

        private List<SetupTask> _tasks = new List<SetupTask>()
            {
                new SetupTask() { 
                    Code = "DbConn",
                    Description = "DbConnection AppSetting",
                    Resolution = "Missing AppSetting DbConnection. Edit your web.config to include <br/>" + 
                                 "&nbsp;&nbsp;&lt;configuration&gt;<br/>" + 
                                 "&nbsp;&nbsp;&nbsp;&nbsp;&lt;appSettings&gt;<br/>" + 
		                         "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;add key=\"DbConnection\" value=\"Data Source=YourDbFile.db3\"/&gt;<br/>" +
	                             "&nbsp;&nbsp;&nbsp;&nbsp;&lt;/appSettings&gt;<br/>" + 
                                 "&nbsp;&nbsp;&lt;/configuration&gt;<br/>",
                    GetState = () =>
                    {
                        if (string.IsNullOrWhiteSpace( ConfigurationManager.AppSettings["DbConnection"]))
                            return TaskState.Stop;

                        return TaskState.Ok;
                    }},

                new SetupTask() { 
                    Code = "AdminPwd",
                    Description = "AdminPwd Login",
                    Resolution = "Create an admin login (Logins -> Add), then give them Admin permission (Accounts -> click 'User'). Remove AdminPwd setting (in Settings)",
                    GetState = () =>
                    {
                        if (!string.IsNullOrWhiteSpace( Config.Current["AdminPwd"]))
                            return TaskState.Warning;

                        return TaskState.Ok;
                    }},

                new SetupTask() { 
                    Code = "BaseUrl",
                    Description = "BaseUrl setting",
                    Resolution = "Need BaseUrl setting. Click 'Try fix' to set it automatically",
                    GetState = () =>
                    {
                        if (string.IsNullOrWhiteSpace(Config.Current["BaseUrl"]))
                            return TaskState.Warning;

                        return TaskState.Ok;
                    },
                    AutoFix = () =>
                    {
                        Config.Current["BaseUrl"] = baseUrl;
                    }
                },

                new SetupTask() { 
                    Code = "UploadFolder",
                    Description = "Upload Folder Settings",
                    Resolution = "Add a setting for the Upload Folder. Click 'Try Fix' to set it to 'files'",
                    GetState = () =>
                    {
                        if (string.IsNullOrWhiteSpace( Config.Current["UploadFolder"]))
                            return TaskState.Warning;

                        return TaskState.Ok;
                    },
                    AutoFix = () => 
                    {
                        string filePath = HttpContext.Current.Server.MapPath("/files");
                        try
                        {
                            if (!Directory.Exists(filePath))
                                Directory.CreateDirectory(filePath);

                            Config.Current["UploadFolder"] = "/files";
                        }
                        catch
                        {}
                    }
                },

                new SetupTask() { 
                    Code = "UploadAccess",
                    Description = "Upload Folder Access",
                    Resolution = "In Windows Explorer, allow Read/Write/Modify on Upload Folder.",
                    GetState = () =>
                    {
                        try
                        {
                            string filename = Path.Combine(Config.UploadFolder, Guid.NewGuid().ToString() + ".tmp");
                            using (var writer = File.CreateText(filename))
                                writer.WriteLine("Hello");

                            File.Delete(filename);
                            return TaskState.Ok;
                        }
                        catch
                        {
                            return TaskState.Error;
                        }                        
                    }},

                new SetupTask() { 
                    Code = "Homepage",
                    Description = "No homepage defined",
                    Resolution = "Click 'Try fix' to create a default homepage. You can edit it later.<br/><b>NOTE</b> the default homepage will contain YOUR ADMIN password.",
                    GetState = () => 
                    {
                        return (PageResponse.Read(PageResponseSpecialNames.Homepage) == null)
                            ? TaskState.Error
                            : TaskState.Ok;
                    },
                    AutoFix = () => 
                        {
                            Template home = new Template() { 
                                Name = "Welcome", 
                                RenderEngineCode = "razor",
                                Body = string.Format("<html><body>This is your default homepage. To login as admin, click <a href=\"edit/loginpage.aspx?as=1&pwd={0}\">admin login</a>.</body><html>", 
                                        Config.Current["AdminPwd"])
                            };

                            home.Save();
                
                            PageResponse page = 
                                new PageResponse()  {
                                    Url= PageResponseSpecialNames.Homepage,
                                    Render = PageRender.Template, 
                                    RenderOptions = (new TemplateRenderParameters() { TemplateId = home.Id, CacheDurationSeconds = 100 }).Serialize()};

                            page.Save();
                        }
                    },

                new SetupTask() { 
                    Code = "Four0Four", 
                    Description = "404 page defined",
                    Resolution = "Click 'Try fix' to create a default 404 page. You can edit it later",
                    GetState = () => 
                        {
                            if (PageResponse.Read(PageResponseSpecialNames.FourOhFour) == null)
                                return TaskState.Warning;

                            return TaskState.Ok;
                        },
                    AutoFix = () =>
                        {
                            Template four0four = new Template() { 
                                Name = "404", 
                                RenderEngineCode = "razor",
                                Body = "<html><body>This is your default 404 page. </body><html>"
                            };

                            four0four.Save();
                
                            PageResponse page = 
                                new PageResponse()  {
                                    Url= PageResponseSpecialNames.FourOhFour,
                                    Render = PageRender.Template,
                                    RenderOptions = (new TemplateRenderParameters() { TemplateId = four0four.Id, CacheDurationSeconds = 100 }).Serialize()
                                };

                            page.Save();
                        }
                    },

                new SetupTask() 
                {
                    Code = "AdminRole", 
                    Description = "Admin Role Defined",
                    Resolution = "Click 'Try Fix' to add Admin Role",
                    GetState = () => 
                        {
                            return Role.ForCode("Admin") == null
                                ? TaskState.Error
                                : TaskState.Ok;
                        },
                    AutoFix = () => 
                    {
                            Role admin = Role.ForCode("Admin");
                            if (admin == null)
                            {
                                admin = new Role() { Code = "Admin", Description = "Administrator" };
                                admin.Save();

                                Permission permission = new Permission() { Code = "Admin" };
                                permission.Save();

                                RolePermission.AddRolePermission(admin.Id, permission.Id);
                            }
                    }
                },

            new SetupTask() 
                {
                    Code = "AdminPpl", 
                    Description = "Login with Admin Role",
                    Resolution = "No users have access to Admin role. Click 'Try Fix' to create a Password and Hack",
                    GetState = () => 
                        {
                            if (Role.ForCode("Admin") == null)
                                return TaskState.Error;

                            if (!AccountRole.AnyRoleIdWhere(Role.ForCode("Admin").Id))
                                return TaskState.Error;

                            return TaskState.Ok;
                        },
                    AutoFix = () => 
                        {
                            Role admin = Role.ForCode("Admin");
                            if (admin == null)
                                return;

                            if (AccountRole.AnyRoleIdWhere(admin.Id))
                                return;

                            Account account = new Account() { DisplayName = "Temp Admin Account" };
                            account.Save();
                            account.RoleAdd(admin);
                        
                            // Set the AdminPwd (backdoor login hack)
                            Config.Current["AdminPwd"] = "abcdefghjklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTVYZ".PickRandomly(10);
                        }
                },
                new SetupTask() { 
                    Code = "DisableSetup",
                    Description = "Disable Setup Mode",
                    Resolution = "For added security, click 'Try Fix' to disable setup mode. This will STOP this page functioning (this is unable by an other mechanism)",
                    GetState = () =>
                    {
                        if (Config.Current["ChgDoc.DisableSetup"] != "true")
                            return TaskState.Warning;

                        return TaskState.Ok;
                    },
                    AutoFix = () => 
                    {
                        Config.Current["ChgDoc.DisableSetup"] = "true";
                    }

                }
            };

		protected void Page_Load( object sender, EventArgs e )
		{
            if (IsPostBack)
                return;

            if (Config.Current.DisableSetup)
            {
                litTasks.Text = "<h2>Setup Mode is disabled</h2>";
                return;
            }

            baseUrl = HttpContext.Current.Request.Url.ToString().Substring(0, HttpContext.Current.Request.Url.ToString().Length - HttpContext.Current.Request.Url.PathAndQuery.Length) + (HttpContext.Current.Request.ApplicationPath == "/" ? "/" : HttpContext.Current.Request.ApplicationPath + "/");

            btnAdminLogin.Visible = (!Config.Current.DisableSetup && !string.IsNullOrWhiteSpace(Config.Current["AdminPwd"]));

            _tasks.ForEach(t => t.State = TaskState.Unknown);

            if (!String.IsNullOrWhiteSpace(Request.QueryString["fix"]))
                Fix(Request.QueryString["fix"]);

            StringBuilder html = new StringBuilder();
            foreach (var task in _tasks)
            {
                html.AppendFormat("<div class='messageBox {0}'><div><img src=\"{1}\"><span>{2} - <b>{0}</b> </span>{5}<div {3}>{4}</div></div></div>",
                    task.State, ImageForStatus(task.State), task.Description, 
                    task.State == TaskState.Ok ? "style='display:none;'" : "" ,
                    task.Resolution,
                    (task.State != TaskState.Ok && task.AutoFix != null) ? "<a href=\"?fix=" + task.Code + "\">Try-Fix</a>" : ""
                    );

                if (task.State == TaskState.Stop)
                    break;
            }

            litTasks.Text = html.ToString();
		}

        private void Fix(string code)
        {
            SetupTask task = _tasks.Single(t => string.Equals(t.Code, code, StringComparison.OrdinalIgnoreCase));
            if (task.State == TaskState.Ok)
                return;

            if (Config.Current["ChgDoc.DisableSetup"] == "true")
                return;

            if (task.AutoFix != null)
            {
                task.AutoFix();
                task.State = TaskState.Unknown;
            }
        }

        private string ImageForStatus(TaskState state)
        {
            if (iconUrl == null)
            {
                iconUrl = Request.RawUrl.Substring(0, Request.RawUrl.IndexOf("/edit/", StringComparison.OrdinalIgnoreCase))
                        + "/edit/content/img/icon/";
            }

            switch (state)
            {
                case TaskState.Ok:
                    return iconUrl + "info.png";
                case TaskState.Error:
                case TaskState.Stop:
                    return iconUrl + "error.png";
                default:
                    return iconUrl + "warning.png";
            }
        }

        private string iconUrl;
        private static string baseUrl;

        protected void btnAdminLogin_click(object sender, EventArgs e)
        {
            if (!Config.Current.DisableSetup && !string.IsNullOrWhiteSpace(Config.Current["AdminPwd"]))
            {
                Account acc = Account.Read(1);
                Visitor.SetAccount(acc);
                Response.Redirect("~/edit/setuptasks.aspx", true);                
            }
        }
	}
}